// /////////////////////////////////////////////////////////////////////////////
//
// Name:            CubePanel.cpp
// Author:          Michael Bartsch
//
// Desc :           This file contains the CCubePanel class. This panel
//                  displays a 3d analysis of the image.
//
// License:         Copyright (C) 2008-2009 Michael Bartsch
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the GNU General Public
//                  License as published by the Free Software Foundation,
//                  either version 3 of the License, or (at your option) any
//                  later version.
//
//                  This program is distributed in the hope that it will be
//                  useful, but WITHOUT ANY WARRANTY; without even the implied
//                  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//                  PURPOSE. See the GNU General Public License for more
//                  details.
//
//                  You should have received a copy of the GNU General Public
//                  License along with this program. If not, see
//                  <http://www.gnu.org/licenses/>.
//
// Reference:       Drawing Image RGB Color Distribution Using OpenGL by
//                  Pierre Alliez and Magali Mazihre
//
//                  For the original code, see:
//                  <http://www.codeguru.com/cpp/g-m/opengl/article.php/c2691>
//
// /////////////////////////////////////////////////////////////////////////////

// Include required header file(s).
#include "CubePanel.h"


// CCubePanel Events. - [ BEGIN ]
BEGIN_EVENT_TABLE(CCubePanel, wxGLCanvas)
   EVT_PAINT(CCubePanel::onPaint)

   EVT_LEFT_DOWN(CCubePanel::onLeftDown)
   EVT_LEFT_UP(CCubePanel::onLeftUp)
   EVT_MOTION(CCubePanel::onMouseMotion)
END_EVENT_TABLE()
// CCubePanel Events. - [ END ]


// CCubePanel.
// Constructor.
CCubePanel::CCubePanel(wxWindow *parent)
: wxGLCanvas(parent, wxID_ANY, wxDefaultPosition, wxSize(385, 288), wxFULL_REPAINT_ON_RESIZE)
{
    pColourAnalyser = CColourAnalyser::Instance();

    isInitGL = false;
    isLeftDown = false;

    rotateX = rotateY = 0;
}

// Init OpenGL.
void CCubePanel::initGL()
{
    SetCurrent();

    // Set OpenGL options.
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);

	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);

	glLineWidth(1.5);
	glPointSize(1.0);

	glPolygonMode(GL_FRONT,GL_LINE);
	glPolygonMode(GL_BACK,GL_LINE);

    glShadeModel(GL_SMOOTH);

    // Set viewing project.
    glMatrixMode(GL_PROJECTION);
    glFrustum(-0.5f, 0.5f, -0.5f, 0.5f, 1.0f, 3.0f);

    // Position viewer.
    glMatrixMode(GL_MODELVIEW);
    glTranslatef(0.0f, 0.0f, -2.0f);

    // Create the RGB Cube and the Colour Cloud.
    createCube();
    createCloud();
}

// Rendering
void CCubePanel::Render()
{
    // Create a DC.
    wxPaintDC *PanelDC = new wxPaintDC(this);

#ifndef __WXMOTIF__
    if (!GetContext()) return;
#endif

    // Makes sure that OpenGL modifies this DC.
    SetCurrent();

    // Init OpenGL.
    if(!isInitGL)
    {
       initGL();
       isInitGL = true;
    }

    // Clear the screen.
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Rendering.
	glPushMatrix();

	 glRotated(rotateX, 1.0, 0.0, 0.0);
	 glRotated(rotateY, 0.0, 1.0, 0.0);

     glCallList(cloudList);   // Cloud
     glCallList(cubeList);   // RGB Cube
 	glPopMatrix();

 	glFlush();
 	SwapBuffers();

    // Tidy up code.
    delete PanelDC;
}

// Creates the RGB Cube.
void CCubePanel::createCube()
{
    // Prepare creation.
    GLUquadricObj* pQuadric = gluNewQuadric();
    float x = 0.5;

    cubeList = glGenLists(1);
    glNewList(cubeList, GL_COMPILE_AND_EXECUTE);

    // RGB cube
    glBegin(GL_LINE_LOOP);
     glColor3ub(0,0,0);
     glVertex3d(-x,-x,-x);
     glColor3ub(255,0,0);
     glVertex3d(x,-x,-x);
     glColor3ub(255,255,0);
     glVertex3d(x,x,-x);
     glColor3ub(0,255,0);
     glVertex3d(-x,x,-x);
    glEnd();

    glBegin(GL_LINE_LOOP);
     glColor3ub(0,0,255);
     glVertex3d(-x,-x,x);
     glColor3ub(255,0,255);
     glVertex3d(x,-x,x);
     glColor3ub(255,255,255);
     glVertex3d(x,x,x);
     glColor3ub(0,255,255);
     glVertex3d(-x,x,x);
    glEnd();

    glBegin(GL_LINES);
     glColor3ub(0,0,0);
     glVertex3d(-x,-x,-x);
     glColor3ub(0,0,255);
     glVertex3d(-x,-x,x);
     glColor3ub(255,0,0);
     glVertex3d(x,-x,-x);
     glColor3ub(255,0,255);
     glVertex3d(x,-x,x);
     glColor3ub(255,255,0);
     glVertex3d(x,x,-x);
     glColor3ub(255,255,255);
     glVertex3d(x,x,x);
     glColor3ub(0,255,0);
     glVertex3d(-x,x,-x);
     glColor3ub(0,255,255);
     glVertex3d(-x,x,x);
    glEnd();

    // Spheres
    glPolygonMode(GL_FRONT,GL_FILL);
    glPolygonMode(GL_BACK,GL_FILL);

    float radius = 0.05f;

    glPushMatrix();
     glTranslated(-x,-x,-x);
     glColor3ub(0,0,0);
     gluSphere(pQuadric,radius,12,12);
    glPopMatrix();

    glPushMatrix();
     glTranslated(x,-x,-x);
     glColor3ub(255,0,0);
     gluSphere(pQuadric,radius,12,12);
    glPopMatrix();

    glPushMatrix();
     glTranslated(-x,x,-x);
     glColor3ub(0,255,0);
     gluSphere(pQuadric,radius,12,12);
    glPopMatrix();

    glPushMatrix();
     glTranslated(-x,-x,x);
     glColor3ub(0,0,255);
     gluSphere(pQuadric,radius,12,12);
    glPopMatrix();

    glPushMatrix();
     glTranslated(x,x,-x);
     glColor3ub(255,255,0);
     gluSphere(pQuadric,radius,12,12);
    glPopMatrix();

    glPushMatrix();
     glTranslated(-x,x,x);
     glColor3ub(0,255,255);
     gluSphere(pQuadric,radius,12,12);
    glPopMatrix();

    glPushMatrix();
     glTranslated(x,-x,x);
     glColor3ub(255,0,255);
     gluSphere(pQuadric,radius,12,12);
    glPopMatrix();

    glPushMatrix();
     glTranslated(x,x,x);
     glColor3ub(255,255,255);
     gluSphere(pQuadric,radius,12,12);
    glPopMatrix();

    glEndList();

    // Tidy up code.
    gluDeleteQuadric(pQuadric);
}

// Creates the RGB Cube.
void CCubePanel::createCloud()
{
    // Check if a camera image exists.
    if(pColourAnalyser->getCameraImage() == NULL)
      return;

    // Create a table to check which colours are already displayed.
    unsigned char *colourTable = new unsigned char[16777216];   // 2^24 = 16777216
    memset(colourTable, 0, 16777216);

    // Create a new list
    cloudList = glGenLists(1);
    glNewList(cloudList, GL_COMPILE_AND_EXECUTE);

    glBegin(GL_POINTS);

    float tmp = 2.0f/255.0f*0.5f;

    for(unsigned int j = 0; j < 352; j++)
    {
       for(unsigned int i = 0; i < 288; i++)
       {
          unsigned char b = pColourAnalyser->getCameraImage()->GetBlue(i, j);
          unsigned char g = pColourAnalyser->getCameraImage()->GetGreen(i, j);
          unsigned char r = pColourAnalyser->getCameraImage()->GetRed(i, j);

          if(!colourTable[b*65536+g*256+r])
          {
             glColor3ub(r, g, b);

             float x = -0.5f+(float)r*tmp;
             float y = -0.5f+(float)g*tmp;
             float z = -0.5f+(float)b*tmp;

             glVertex3d(x, y, z);
             colourTable[b*65536+g*256+r] = 1;
          }
       }
    }

    glEnd();
    glEndList();

    // Tidy up code.
    delete[] colourTable;
}

// Event Functions. - [ BEGIN ]
void CCubePanel::onPaint(wxPaintEvent &event)
{
    Render();
}

void CCubePanel::onLeftDown(wxMouseEvent &event)
{
    event.Skip();

    isLeftDown = true;
    clickX = event.GetPosition().x;
    clickY = event.GetPosition().y;
}

void CCubePanel::onLeftUp(wxMouseEvent &event)
{
    isLeftDown = false;
}

void CCubePanel::onMouseMotion(wxMouseEvent &event)
{
    if(isLeftDown)
    {
       rotateY += ((float)event.GetPosition().x-(float)clickX)/176;
       rotateX += ((float)event.GetPosition().y-(float)clickY)/144;
       Render();
    }
}
// Event Functions. - [ END ]

// End of File
